package com.ewili.framework.utils;

/**
 * 异常工具类
 * <p>
 * 部分代码来自guava Throwables
 *
 * @author leo
 */
public final class ExceptionUtils {

    private ExceptionUtils() {
    }

    /**
     * 获取根Cause
     *
     * @param throwable
     * @return
     */
    public static Throwable getRootCause(Throwable throwable) {
        Throwable cause;
        while ((cause = throwable.getCause()) != null) {
            throwable = cause;
        }
        return throwable;
    }

    /**
     * 判断异常是否由某些底层的异常引起.
     *
     * @param throwable
     * @param causeThrowableClasses
     * @return
     */
    @SafeVarargs
    public static boolean isCausedBy(Throwable throwable,
                                     Class<? extends Throwable>... causeThrowableClasses) {
        Throwable cause = throwable;
        while (cause != null) {
            for (Class<? extends Throwable> causeClass : causeThrowableClasses) {
                if (causeClass.isInstance(cause)) {
                    return true;
                }
            }
            cause = cause.getCause();
        }
        return false;
    }

    /**
     * 如果throwable是declaredType的实例，将抛出异常，异常实例就是throwable本身。 如果不是，不作处理。
     *
     * @param throwable
     * @param declaredType
     * @throws X
     */
    public static <X extends Throwable> void propagateIfInstanceOf(
            Throwable throwable, Class<X> declaredType) throws X {
        if (throwable != null && declaredType.isInstance(throwable)) {
            throw declaredType.cast(throwable);
        }
    }

    /**
     * 将checked exception转为unchecked exception，并抛出。
     * 调用此方法将永远抛出异常，设计方法返回值只是为了在调用点方法有返回值时使用方便。如：
     * <p>
     * <pre>
     * String doSomething() {
     * 	try {
     * 		return someMethodThatCouldThrowAnything();
     *    } catch (Throwable t) {
     * 		throw ExceptionUtils.propagate(t);
     *    }
     * }
     * </pre>
     * <p>
     * 具体实现： 如果e是RuntimeException或者Error的实例，将抛出异常，异常实例就是e本身。
     * 其他情况，将抛出RuntimeException异常，并把e包括在此RuntimeException的cause中。
     *
     * @param e
     * @return 永远不会返回任何值，只是为了使用方便
     */
    public static RuntimeException propagate(Throwable e) {
        propagateIfInstanceOf(e, RuntimeException.class);
        propagateIfInstanceOf(e, Error.class);
        throw new RuntimeException(e);
    }

}
